use crate::db::db_school;
use crate::db::entities::school::Model;
use crate::rest_api::api_school::{CreateParams, School, SearchParams, UpdateParams};

use anyhow::Result;

use rbase::define::{ApiError, BaseError};
use rbase::id_utils::str2id;
use rbase_iam::db::define::SYSTEM_ID;
use rbase_iam::rest_api::api_user::UserToken;

pub async fn school_from_model(subject_model: &Model) -> Result<School> {
    let subject = School::from_model(subject_model);
    Ok(subject)
}

pub async fn find_school_by_id(user_token: &UserToken, subject_id: &String) -> Result<School, ApiError> {
    let school_model = db_school::find_by_id_and_owner_ids(str2id(subject_id)?, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(subject_id.to_string()))?;
    Ok(school_from_model(&school_model).await?)
}

pub async fn delete_school_by_id(user_token: &UserToken, subject_id: &String) -> Result<(), ApiError> {
    let school_model = db_school::find_by_id_and_owner_ids(str2id(subject_id)?, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(subject_id.to_string()))?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && school_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    db_school::delete_by_id(school_model.id).await?;
    Ok(())
}

pub async fn update_school_by_id(user_token: &UserToken, subject_id: &String, new_subject: &UpdateParams) -> Result<(), ApiError> {
    let old_model = db_school::find_by_id_and_owner_ids(str2id(subject_id)?, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(subject_id.to_string()))?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && old_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    let new_model = Model {
        id: old_model.id,
        name: Some(new_subject.name.clone()),
        ..Default::default()
    };
    db_school::update(new_model).await?;
    Ok(())
}

pub async fn create_school(user_token: &UserToken, new_school: &CreateParams) -> Result<School, ApiError> {
    let new_model = Model {
        name: Some(new_school.name.clone()),
        owner_id: Some(user_token.id),
        ..Default::default()
    };
    let created_model = db_school::insert(new_model).await?;
    Ok(school_from_model(&created_model).await?)
}

pub async fn search(user_token: &UserToken, school_search_params: &SearchParams) -> Result<(Vec<School>, i64), ApiError> {
    let (schools, total) = db_school::search(&db_school::SearchParams {
        name: school_search_params.name.clone(),
        offset: school_search_params.offset,
        limit: school_search_params.limit,
        order_by: school_search_params.order_by.clone(),
        order: school_search_params.order.clone(),
        owner_ids: user_token.owner_ids.clone(),
    })
    .await?;

    let mut school_results = Vec::new();
    for subject in schools {
        school_results.push(school_from_model(&subject).await?);
    }

    Ok((school_results, total))
}

pub async fn validate_school_id(user_token: &UserToken, school_id: &Option<String>) -> Result<Option<i64>, ApiError> {
    match school_id {
        Some(school_id_str) => {
            let school_id = str2id(&school_id_str)?;
            db_school::find_by_id_and_owner_ids(school_id, &user_token.owner_ids)
                .await?
                .ok_or_else(|| BaseError::ResourceNotFound(school_id_str.to_string()))?;
            Ok(Some(school_id))
        }
        None => Ok(None),
    }
}
